package com.liruilong.hros.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.liruilong.hros.filter.VerifyCodeFilter;
import com.liruilong.hros.model.Hr;
import com.liruilong.hros.model.RespBean;
import com.liruilong.hros.service.HrService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Description :
 */

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    HrService hrService;
    @Autowired
    CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;
    @Autowired
    CustomUrlDecisionManager customUrlDecisionManager;
    @Autowired
    VerifyCodeFilter verifyCodeFilter ;
    @Autowired
    MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(hrService);
    }
    /**
     *
     * #切换最顶级的方法
     * and()
     * #验证请求(认证配置类)
     * authorizeRequests()
     *     #任何请求
     *     anyRequest()
     *         #保护URL需要登录访问
     *         authenticationed()
     *     #匹配某个资源路径
     *     antMatchers（“/path”）
     *         #指定url无需保护(放行)一般用户静态资源
     *         permitAll()
     *         #某个资源需要用户拥有什么样的role才能访问
     *         hasRole("admin")
     *         #某个资源需要用户拥有什么样的权限才能访问
     *         hasAuthority("admin")
     *     #某个资源拥有指定角色中的一个就能访问
     *     hasAnyRole(String ...roles)
     *     #某个资源拥有指定权限中的一个就能访问
     *     hasAnyAuthority(String ... authorities)
     *     #该方法使用SPEL表达式,可以创建复杂的限制
     *     access(String attribute)
     *     #拥有什么样的ip或子网可以访问该资源
     *     hasIpAddress(String ip)
     * #登出
     * logout()
     * 	#触发退出登入的操作访问的url
     * 	logoutUrl("/logout")
     * 	#成功登出的请求地址
     * 	logoutSuccessUrl("/index")
     * 	#或者成功登出的response操作处理器
     * 	logoutSuccessHandler(new MyLogoutSuccessHandler())
     * 	#是否登出的时候让Session失效 默认为true
     * 	invalidateHttpSession(true)
     * 	#删除cookie
     * 	deleteCookies("cookieNamesToClear" ...)
     * 	#添加登出时的处理器
     * 	addLogoutHandler(new MyAddLogoutHandler())
     * #异常处理
     * exceptionHandling()
     * 	#权限不足回调处理器
     * 	accessDeniedHandler(new MyAccessDeniedHandler())
     * 	#匿名访问权限不足回调处理器
     * 	authenticationEntryPoint(new MyAuthenticationEntryPoint())
     * #表单处理
     * formLogin()
     * 	#认证成功后的请求地址
     * 	successForwardUrl("/loginSuccess")
     * 	#认证成功的请求处理器
     * 	successHandler(new MySuccessHandler())
     * 	#认证失败的请求处理器
     * 	failureHandler(new MyFailureHandler())
     * 	#设置登入页面路径
     * 	loginPage("/login.html")
     * 	#设置成功登入后跳转的地址 与loginPage一起使用
     * 	loginProcessingUrl("/loginsuccess")
     * #记住登入操作
     * rememberMe()
     * 	#是否持久化 persistentTokenRepository需要要配置
     * 	tokenRepository(persistentTokenRepository)
     * 	#设置过期时间
     * 	tokenValiditySeconds(7200)
     * 	#加载用户认证信息 userDetailsService获取数据库中的账户信息
     * 	userDetailsService(userDetailsService)
     * @Description  放行的请求路径
     * @Date 19:25 2020/2/7
     * @Param [web]
     * @return void
     **/

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/auth/code","/login","/css/**","/js/**", "/index.html", "/img/**", "/fonts/**","/favicon.ico");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                //.anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                //修改Spring Security默认的登陆界面
                .and().formLogin().usernameParameter("username").passwordParameter("password") .loginProcessingUrl("/doLogin")
                .loginPage("/login")
                //登录成功回调
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        Hr hr = (Hr) authentication.getPrincipal();
                        //密码不回传
                        hr.setPassword(null);
                        RespBean ok = RespBean.ok("登录成功！", hr);
                        //将hr转化为Sting
                        String s = new ObjectMapper().writeValueAsString(ok);
                        out.write(s);
                        out.flush();
                        out.close();
                    }
                })
                //登失败回调
                .failureHandler(myAuthenticationFailureHandler)
                //相关的接口直接返回
                .permitAll()
                .and()
                .logout()
                //注销登录
                // .logoutSuccessUrl("")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                                HttpServletResponse httpServletResponse,
                                                Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功!")));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .csrf().disable().exceptionHandling()
                //没有认证时，在这里处理结果，不要重定向
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        resp.setStatus(401);
                        PrintWriter out = resp.getWriter();
                        RespBean respBean = RespBean.error("访问失败!");
                        if (authException instanceof InsufficientAuthenticationException) {
                            respBean.setMsg("请求失败，请联系管理员!");
                        }
                        out.write(new ObjectMapper().writeValueAsString(respBean));
                        out.flush();
                        out.close();
                    }
                });
    }
}